package com.lagou.edu.factory;

import com.alibaba.druid.support.json.JSONUtils;
import com.lagou.edu.stereotype.*;
import com.lagou.edu.utils.TransactionManager;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.*;
import java.net.URL;
import java.util.*;

/**
 * @author 应癫
 * <p>
 * 工厂类，生产对象（使用反射技术）
 */
public class BeanFactory {


    private static final String CLASS_SUFFIX = ".class";
    private static final String CLASS_FILE_PREFIX = File.separator + "classes" + File.separator;
    private static final String PACKAGE_SEPARATOR = ".";

    private static Map<String, Object> map = new HashMap<>();  // 存储对象

    /**
     * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */

    static {
        // 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
        // 加载xml
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        // 解析xml
        SAXReader saxReader = new SAXReader();
        List<String> transClassList = new ArrayList<>();
        List<Map<String, Field>> fieldList = new ArrayList<>();
        List<Method> methodList = new ArrayList<>();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> beanList = rootElement.selectNodes("//component-scan");
            Element element = beanList.get(0);
            // 处理每个bean元素，获取到该元素的id 和 class 属性
            String basePackage = element.attributeValue("base-package");
            List<String> classPathList = getClassPath(basePackage);

            for (String classPath : classPathList) {
                String id = "";
                Class<?> clazz = Class.forName(classPath);
                //获取类上面的注解信息
                if (clazz.isAnnotationPresent(Repository.class) || clazz.isAnnotationPresent(Service.class)) {
//                    classList.add(clazz);
                    // 通过反射技术实例化对象
                    if (clazz.isAnnotationPresent(Repository.class)) {
                        Repository repository = clazz.getAnnotation(Repository.class);
                        id = repository.value();
                    }
                    if (clazz.isAnnotationPresent(Service.class)) {
                        Service service = clazz.getAnnotation(Service.class);
                        id = service.value();
                    }
                    Object o = clazz.newInstance();  // 实例化之后的对象
                    if ("".equals(id)) {
                        //输入没有value  将接口名称拿来  将首字母小写作为key
                        //判断类是不是接口
                        String name = "";
                        if (clazz.isInterface()) {
                            name = clazz.getName();
                        } else {
                            //TODO 多个接口的
                            name = clazz.getInterfaces()[0].getName();
                        }
                        name = name.substring(name.lastIndexOf(".") + 1);
                        char[] chars = name.toCharArray();
                        chars[0] += 32;
                        id = String.valueOf(chars);
                    }
                    // 存储到map中待用
                    map.put(id, o);

                } else if (clazz.isAnnotationPresent(Component.class)) {
                    Component component = clazz.getAnnotation(Component.class);
                    Object o = clazz.newInstance();  // 实例化之后的对象
                    id = component.value();
                    if ("".equals(id)) {
                        //输入没有value  将接口名称拿来  将首字母小写作为key
                        //判断类是不是接口
                        String name  = clazz.getName();
                        name = name.substring(name.lastIndexOf(".") + 1);
                        char[] chars = name.toCharArray();
                        chars[0] += 32;
                        id = String.valueOf(chars);
                    }
                    // 存储到map中待用
                    map.put(id, o);
                }

                //获取类里面对象注解信息
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(Autowired.class)) {
                        Map<String, Field> map = new HashMap<>();
                        map.put(id, field);
                        fieldList.add(map);
                    }
                }
                //TODO  事务注解在方法上面
                //获取当前类上所有方法是否带了注解
                /*Method[] methods = clazz.getMethods();
                for(Method method:methods){
                    if(method.isAnnotationPresent(Transactional.class)){
                        methodList.add(method);
                    }
                }*/


                //事务注解在类上面
                if (clazz.isAnnotationPresent(Transactional.class)) {
//                    Map<String,Class<?>> map = new HashMap<>();
//                    map.put(id,clazz);
                    transClassList.add(id);
                }
            }
            //将autowired注解的类实现赋值
            for (Map<String, Field> fieldMap : fieldList) {
                for (String key : fieldMap.keySet()) {
                    Field field = fieldMap.get(key);
                    String name = field.getName();
                    //从map中获取该key的对象
                    Object o = map.get(name);
                    //为类实现set方法
                    field.setAccessible(true);
                    Object o1 = field.getDeclaringClass().newInstance();
                    field.set(o1, o);
                    map.put(key, o1);
                }
            }
            //将transfer进行解析
            for (String key : transClassList) {
                //从map中获取该key的对象
                Object o = map.get(key);
                //为类实现set方法
                Object o1 = getJdkProxy(o);
                map.put(key, o1);
            }


        } catch (ClassNotFoundException e) {
            e.getStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }catch (DocumentException e){
            e.printStackTrace();
        }

    }

//    public static void init(){
//        aa();
//    }


    private static void aa() {

    }


    private static Object getJdkProxy(Object obj) {

        // 获取代理对象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object result = null;

                        //TODO 类写死在方法中  后续优化
                        TransactionManager transactionManager = (TransactionManager) map.get("transactionManager");
                        try {
                            // 开启事务(关闭事务的自动提交)
                            transactionManager.beginTransaction();
                            result = method.invoke(obj, args);
                            // 提交事务
                            transactionManager.commit();
                        } catch (Exception e) {
                            e.printStackTrace();
                            // 回滚事务
                            transactionManager.rollback();
                            // 抛出异常便于上层servlet捕获
                            throw e;
                        }
                        return result;
                    }
                });

    }


    /**
     * 获取指定包下所有的类路径
     *
     * @return
     */
    private static List<String> getClassPath(String packageName) {
//        String packageName = "com.lagou.edu";
        List<String> result = new LinkedList<>();
//        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        ClassLoader loader = BeanFactory.class.getClassLoader();
        String suffixPath = packageName.replaceAll("\\.", "/");
        try {
            Enumeration<URL> urls = loader.getResources(suffixPath);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                if (url != null) {
                    String protocol = url.getProtocol();
                    if ("file".equals(protocol)) {
                        String path = url.getPath();
                        result.addAll(getAllClassNameByFile(new File(path)));
                    } else if ("jar".equals(protocol)) {
                        //TODO 暂不处理jar
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }


    /**
     * 递归获取类名称
     *
     * @param file
     * @return
     */
    private static List<String> getAllClassNameByFile(File file) {
        List<String> result = new ArrayList<>();
        if (!file.exists()) {
            return result;
        }
        if (file.isFile()) {
            String path = file.getPath();
            // 注意：这里替换文件分割符要用replace。因为replaceAll里面的参数是正则表达式,而windows环境中File.separator="\\"的,因此会有问题
            if (path.endsWith(CLASS_SUFFIX)) {
                path = path.replace(CLASS_SUFFIX, "");
                // 从"/classes/"后面开始截取
                String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length()).replace(File.separator, PACKAGE_SEPARATOR);
                if (-1 == clazzName.indexOf("$")) {
                    result.add(clazzName);
                }
            }
            return result;
        } else {
            File[] listFiles = file.listFiles();
            if (listFiles != null && listFiles.length > 0) {
                for (File f : listFiles) {
                    result.addAll(getAllClassNameByFile(f));
                }
            }
            return result;
        }
    }


    public static void main(String[] args) {
        aa();
    }


    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static Object getBean(String id) {
        return map.get(id);
    }

}
